﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.IO;
using System.Data;
using System.ComponentModel;
using System.Windows.Data;

namespace TextAnalyzer
{
    internal enum ModeType
    {
        KeyHeader = 1,
        Postfix,
        Category
    }

    internal class ModeTypeHelper
    {
        private static ModeType[] mTypes = new ModeType[] { ModeType.KeyHeader, ModeType.Postfix, ModeType.Category };
        public static ModeType[] Types
        {
            get
            {
                return mTypes;
            }
        }
    }

    internal class TextViewModel:INotifyPropertyChanged
    {
        private static TextViewModel mInstance = null;
        public event PropertyChangedEventHandler PropertyChanged;
        public ObservableCollection<KeyViewModel> Keys { get; set; }
        public DataTable Contents { get; set; }
        public ObservableCollection<PostfixViewModel> Postfix { get; set; }
        public ObservableCollection<KeyViewModel> PostfixParents { get; set; }
        public ObservableCollection<CategoryViewModel> Categories { get; set; }
        public ObservableCollection<KeyViewModel> CategoryParents { get; set; }
        public ICollectionView PostfixView { get; set; }
        public ICollectionView CategoryView { get; set; }
        public ExportType ExportTarget { get; set; }
        private KeyViewModel mPostfixParent = null;
        private KeyViewModel mCategoryParent = null;
        private List<Dictionary<string, string>> mAnalyzeds = null;
        private string mPath = "Configuration.txt";
        private string mModeHeader = "#";
        public KeyViewModel PostfixParent
        {
            get { return mPostfixParent; }
            set
            {
                mPostfixParent = value;
                RaisePropertyChanged("PostfixParent");
                PostfixView.Refresh();
            }
        }
        public KeyViewModel CategoryParent
        {
            get { return mCategoryParent; }
            set
            {
                mCategoryParent = value;
                RaisePropertyChanged("CategoryParent");
                CategoryView.Refresh();
            }
        }
        private TextViewModel()
        {
            if (null == Keys) Keys = new ObservableCollection<KeyViewModel>();
            if (null == Postfix) Postfix = new ObservableCollection<PostfixViewModel>();
            if (null == Categories) Categories = new ObservableCollection<CategoryViewModel>();
            if (null == Contents) Contents = new DataTable("Contents");
            if (null == PostfixParents) PostfixParents = new ObservableCollection<KeyViewModel>();
            if (null == CategoryParents) CategoryParents = new ObservableCollection<KeyViewModel>();
            PostfixView = CollectionViewSource.GetDefaultView(Postfix);
            PostfixView.Filter = PostfixFilter;
            CategoryView = CollectionViewSource.GetDefaultView(Categories);
            CategoryView.Filter = CategoryFilter;
            Keys.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(OnKeysCollectionChanged);

            string _line = null;
            StreamReader _reader = null;

                if (File.Exists(mPath))
                {
                    _reader = new StreamReader(mPath);
                    ModeType _mode = 0;
                    while (null != (_line = _reader.ReadLine()))
                    {
                        if (_line.Trim().StartsWith(mModeHeader))
                        {
                            string _label = _line.Remove(0, 1).Trim();
                            foreach (ModeType _type in ModeTypeHelper.Types)
                            {
                                if (_label == _type.ToString())
                                {
                                    _mode = _type;
                                    break;
                                }
                            }
                        }
                        else
                        {
                            if (0 < _mode)
                            {
                                if (0 < _line.Length)
                                {
                                    switch (_mode)
                                    {
                                        case ModeType.KeyHeader:
                                            MakeKeyHeader(_line);
                                            break;
                                        case ModeType.Postfix:
                                            MakePostfix(_line);
                                            break;
                                        case ModeType.Category:
                                            MakeCategory(_line);
                                            break;
                                    }
                                }
                            }
                        }
                    }
                }
        }

        private void OnKeysCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            UpdateChanged();
        }

        private void UpdateChanged()
        {
            Dictionary<string, KeyViewModel> _postfixes = new Dictionary<string, KeyViewModel>();
            Dictionary<string, KeyViewModel> _categories = new Dictionary<string, KeyViewModel>();
            foreach (KeyViewModel _model in Keys)
            {
                switch (_model.Type)
                {
                    case SearchType.Postfix:
                        _postfixes.Add(_model.Key, _model);
                        break;
                    case SearchType.Category:
                        _categories.Add(_model.Key, _model);
                        break;
                }
            }

            for (int i = PostfixParents.Count; i > 0; i--)
            {
                KeyViewModel _parent = PostfixParents[i - 1];
                if (!_postfixes.ContainsKey(_parent.Key)) PostfixParents.RemoveAt(i - 1);
            }
            foreach (string _key in _postfixes.Keys)
            {
                bool _exist = false;
                foreach (KeyViewModel _parent in PostfixParents)
                {
                    if (_parent.Key == _key)
                    {
                        _exist = true;
                        break;
                    }
                }
                if (!_exist) PostfixParents.Add(_postfixes[_key]);
            }

            for (int i = CategoryParents.Count; i > 0; i--)
            {
                KeyViewModel _parent = CategoryParents[i - 1];
                if (!_categories.ContainsKey(_parent.Key)) CategoryParents.RemoveAt(i - 1);
            }
            foreach (string _key in _categories.Keys)
            {
                bool _exist = false;
                foreach (KeyViewModel _parent in CategoryParents)
                {
                    if (_key == _parent.Key)
                    {
                        _exist = true;
                        break;
                    }
                }
                if (!_exist) CategoryParents.Add(_categories[_key]);
            }
        }

        private void MakeKeyHeader(string strLine)
        {
            List<string> _splits = SplitLine(strLine);
            if (null != _splits)
            {
                KeyViewModel _model = new KeyViewModel(_splits[0], _splits[1]);
                if (2 < _splits.Count) _model.Type = SearchTypeHelper.FromString(_splits[2]);
                if (3 < _splits.Count) _model.Entry = bool.Parse(_splits[3]);
                _model.PropertyChanged += new PropertyChangedEventHandler(OnPropertyChangedKeyViewModel);
                Keys.Add(_model);
                string _compensated = _model.Key;
                if (_compensated.EndsWith(".")) _compensated = _compensated.Remove(_compensated.Length - 1);
                Contents.Columns.Add(_compensated, typeof(string));
            }
        }

        private void MakePostfix(string strLine)
        {
            if (!string.IsNullOrEmpty(strLine))
            {
                string _line = strLine.Trim();
                if (0 < _line.Length){
                    List<string> _splits = SplitLine(_line);
                    if (null != _splits) Postfix.Add(new PostfixViewModel(_splits[0], bool.Parse(_splits[1]), _splits[2]));
                }
            }
        }

        private void MakeCategory(string strLine)
        {
            if (!string.IsNullOrEmpty(strLine))
            {
                string _line = strLine.Trim();
                if (0 < _line.Length)
                {
                    List<string> _splits = SplitLine(_line);
                    if (null != _splits) Categories.Add(new CategoryViewModel(_splits[0], bool.Parse(_splits[1]), _splits[2]));
                }
            }
        }

        private List<string> SplitLine(string strLine)
        {
            List<string> _analyzed = null;
            string[] _splits = strLine.Split(new char[] { '=' });
            if (null == _splits) throw new Exception("파일이 잘못되었습니다.");
            else
            {
                if (3 > _splits.Length) throw new Exception("파일이 잘못되었습니다.");
                else _analyzed = new List<string>(_splits);
            }
            return _analyzed;
        }

        ~TextViewModel()
        {
            StreamWriter _writer = null;
            if (null != Keys)
            {
                if (0 < Keys.Count)
                {
                    if (null==_writer) _writer = new StreamWriter(mPath, false);
                    _writer.WriteLine(mModeHeader + " " + ModeType.KeyHeader.ToString());
                    foreach (KeyViewModel _key in Keys) _writer.WriteLine(_key.Key + "=" + _key.Header + "=" + SearchTypeHelper.ToString(_key.Type) + "=" + _key.Entry.ToString());
                }
            }
            if (null != Postfix)
            {
                if (0 < Postfix.Count)
                {
                    if (null == _writer) _writer = new StreamWriter(mPath, false);
                    else _writer.WriteLine();
                    _writer.WriteLine(mModeHeader + " " + ModeType.Postfix.ToString());
                    foreach (PostfixViewModel _postfix in Postfix) _writer.WriteLine(_postfix.Value + "=" + _postfix.CaseComparison.ToString() + "=" + _postfix.Parent);
                }
            }
            if (null != Categories)
            {
                if (0 < Categories.Count)
                {
                    if (null == _writer) _writer = new StreamWriter(mPath, false);
                    else _writer.WriteLine();
                    _writer.WriteLine(mModeHeader + " " + ModeType.Category.ToString());
                    foreach (CategoryViewModel _category in Categories) _writer.WriteLine(_category.Category + "=" + _category.CaseComparison.ToString() + "=" + _category.Parent);
                }
            }
            if (null != _writer) _writer.Close();
        }

        public static TextViewModel GetInstance()
        {
            if (null == mInstance) mInstance = new TextViewModel();
            return mInstance;
        }

        private bool PostfixFilter(object item)
        {
            bool _filtered = false;
            PostfixViewModel _model = item as PostfixViewModel;
            if (null != _model) if (null != PostfixParent) if (_model.Parent == PostfixParent.Key) _filtered = true;
            return _filtered;
        }

        private bool CategoryFilter(object item)
        {
            bool _filtered = false;
            CategoryViewModel _model = item as CategoryViewModel;
            if (null != _model) if (null != CategoryParent) if (_model.Parent == CategoryParent.Key) _filtered = true;
            return _filtered;
        }

        private KeyViewModel GetRegion(string strPreviousKey, string strNextKey)
        {
            KeyViewModel _region = null;
            int _index = -1;
            if (!string.IsNullOrEmpty(strPreviousKey))
            {
                for (int i = 0; i < Keys.Count; i++)
                {
                    if (strPreviousKey == Keys[i].Key)
                    {
                        _index = i;
                        break;
                    }
                }
            }
            if ((_index + 2) < Keys.Count)
            {
                if (strNextKey == Keys[_index + 2].Key && SearchType.Region == Keys[_index + 1].Type) _region = Keys[_index + 1];
            }
            return _region;
        }

        private KeyViewModel GetEntry()
        {
            KeyViewModel _entry = null;
            foreach (KeyViewModel _key in Keys)
            {
                if (_key.Entry)
                {
                    if (null == _entry) _entry = _key;
                    else throw new Exception("기준은 하나 뿐이어야 합니다.");
                }
            }
            return _entry;
        }

        private Dictionary<string, string> GetRow(string strKey)
        {
            Dictionary<string, string> _row = null;
            KeyViewModel _entry = GetEntry();
            if (null != _entry)
            {
                if (strKey == _entry.Key)
                {
                    _row = new Dictionary<string, string>();
                    if (null == mAnalyzeds) mAnalyzeds = new List<Dictionary<string, string>>();
                    mAnalyzeds.Add(_row);
                }
            }
            return _row;
        }

        public void ImportFile(string strPath)
        {
            if (null == GetEntry()) throw new Exception("기준이 없으면 분석할 수 없습니다.");
            if (File.Exists(strPath))
            {
                StreamReader _reader = new StreamReader(strPath);
                string _line = null;
                Dictionary<string, string> _analyzed = null;
                string _buffer = null;
                string _unfinished = null;
                string _previous = null;
                string _name = null;
                Dictionary<string, string> _categories = new Dictionary<string, string>();

                while (null != (_line = _reader.ReadLine()))
                {
                    bool _append = true;
                    string _compensated = null;
                    if (!string.IsNullOrEmpty(_buffer)) _compensated = _buffer.Trim();

                    foreach (KeyViewModel _key in Keys)
                    {
                        switch (_key.Type)
                        {
                            case SearchType.Label:
                                string _header = _key.Key + ":";
                                if (_line.StartsWith(_header))
                                {
                                    KeyViewModel _region = GetRegion(_previous, _key.Key);
                                    if (null == _region)
                                    {
                                        if (_key.Entry) _analyzed = GetRow(_key.Key);
                                    }
                                    else
                                    {
                                        if (_region.Entry)
                                        {
                                            _analyzed = GetRow(_region.Key);
                                            if (0 < _categories.Count)
                                            {
                                                foreach (string _category in _categories.Keys) _analyzed.Add(_category, _categories[_category]);
                                            }
                                        }
                                        if (null != _analyzed) _analyzed.Add(_region.Key, _compensated);
                                        _buffer = null;
                                        _unfinished = null;
                                    }

                                    if (null != _unfinished)
                                    {
                                        _analyzed.Add(_unfinished, _compensated);
                                        _unfinished = null;
                                    }

                                    _previous = _key.Key;
                                    if (null != _analyzed)
                                    {
                                        string _content = _line.Remove(0, _header.Length).Trim();
                                        if (0 < _content.Length)
                                        {
                                            string _value = null;
                                            if (_analyzed.ContainsKey(_key.Key)) _value = _analyzed[_key.Key];
                                            if (string.IsNullOrEmpty(_value)) _analyzed.Add(_key.Key, _content);
                                            else throw new ArgumentException(_name + " 이후 새로운 업체가 인식되지 않았습니다.");
                                        }
                                        else _unfinished = _key.Key;
                                    }
                                    _append = false;
                                }
                                break;

                            case SearchType.Postfix:
                                if (null != Postfix && !string.IsNullOrEmpty(_compensated))
                                {
                                    if (0 < Postfix.Count)
                                    {
                                        foreach (PostfixViewModel _postfix in Postfix)
                                        {
                                            if (_postfix.Parent == _key.Key)
                                            {
                                                string _end = _postfix.Value;
                                                string _content = _compensated;
                                                if (false == _postfix.CaseComparison)
                                                {
                                                    _end = _end.ToLower();
                                                    _content = _content.ToLower();
                                                }
                                                if (_content.EndsWith(_end))
                                                {
                                                    _previous = _key.Key;
                                                    _buffer = null;
                                                    _unfinished = null;
                                                    if (_key.Entry)
                                                    {
                                                        _analyzed = GetRow(_key.Key);
                                                        if (0 < _categories.Count)
                                                        {
                                                            foreach (string _category in _categories.Keys) _analyzed.Add(_category, _categories[_category]);
                                                        }
                                                    }
                                                    if (null != _analyzed)
                                                    {
                                                        _analyzed.Add(_key.Key, _compensated);
                                                        _name = _compensated;
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                                break;
                                
                            case SearchType.Category:
                                if (0 < Categories.Count && !string.IsNullOrEmpty(_compensated))
                                {
                                    foreach (CategoryViewModel _category in Categories)
                                    {
                                        if (_category.Parent == _key.Key)
                                        {
                                            string _inside = _category.Category;
                                            string _content = _compensated;
                                            if (false == _category.CaseComparison)
                                            {
                                                _inside = _inside.ToLower();
                                                _content = _content.ToLower();
                                            }
                                            if (_content.Contains(_inside))
                                            {
                                                _previous = _key.Key;
                                                _buffer = null;
                                                _unfinished = null;
                                                if (_key.Entry) _analyzed = GetRow(_key.Key);
                                                if (_categories.ContainsKey(_key.Key)) _categories[_key.Key] = _category.Category;
                                                else _categories.Add(_key.Key, _category.Category);
                                            }
                                        }
                                    }
                                }
                                break;
                        }
                    }
                    if (_append) _buffer += " " + _line;
                    else _buffer = null;
                }
                _reader.Close();
            }
        }

        public void Update()
        {
            if (null == Contents) Contents = new DataTable();
            else Contents.Clear();
            if (null != mAnalyzeds)
            {
                foreach (Dictionary<string, string> _analized in mAnalyzeds)
                {
                    DataRow _row = Contents.NewRow();
                    foreach (string _key in _analized.Keys)
                    {
                        string _compensated = _key;
                        if (_compensated.EndsWith(".")) _compensated = _compensated.Remove(_compensated.Length - 1);
                        if (Contents.Columns.Contains(_compensated)) _row[_compensated] = _analized[_key];
                    }
                    Contents.Rows.Add(_row);
                }
            }
        }

        private void OnPropertyChangedKeyViewModel(object sender, PropertyChangedEventArgs e)
        {
            if ("Entry" == e.PropertyName)
            {
                KeyViewModel _entry = sender as KeyViewModel;
                if (true == _entry.Entry)
                {
                    foreach (KeyViewModel _model in Keys)
                    {
                        if (_model != _entry) _model.Entry = false;
                    }
                }
            }
            UpdateChanged();
        }

        private void RaisePropertyChanged(string strPropertyName)
        {
            PropertyChangedEventHandler _handler = this.PropertyChanged;
            if (null != _handler) _handler(this, new PropertyChangedEventArgs(strPropertyName));
        }
    }
}
